//
// Created by martin on 2/8/22.
//

#include "Channel.h"
#include "muduo/net/EventLoop.h"
#include "muduo/base/Logging.h"

#include <sstream>

#include <features.h>
#include <poll.h>
#include <assert.h>

using namespace muduo;
using namespace muduo::net;

/*
 * POLLIN: There is data to read.
 * POLLPRI: There is urgent data to read.
 * POLLOUT: Writing now will not block.
 */

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

Channel::Channel(EventLoop *loop, int fd__)
: loop_(loop),
fd_(fd__),
events_(0),
revents_(0),
index_(-1),
logHup_(true),
tied_(false),
eventHandling_(false),
addedToLoop_(false)
{
}

Channel::~Channel()
{
    assert(!eventHandling_);
    assert(!addedToLoop_);
    if (loop_->isInLoopThread())
    {
        assert(!loop_->hasChannel(this));
    }
}

void Channel::tie(const std::shared_ptr<void>& obj)
{
    tie_ = obj;
    tied_ = true;
}

/**
 * 处理激活的Channel事件
 * @details Poller中监听到激活事件的Channel后, 将其加入激活Channel列表,
 * EventLoop::loop根据激活Channel回调对应事件处理函数.
 * @param recevieTime Poller中调用epoll_wait/poll返回后的时间. 用户可能需要该参数.
 */
void Channel::handleEvent(Timestamp recevieTime)
{
    /*
     * shared_ptr通过RAII方式管理对象资源guard
     * weak_ptr::lock可将weak_ptr提升为shared_ptr, 引用计数+1
     */
    std::shared_ptr<void> guard;
    if (tied_)
    {
        /*
         * 为什么使用 tie?
         * 确保在执行事件处理动作时, 所需的对象不会被释放, 但又不能用shared_ptr,
         * 否则可能导致循环引用. 最好使用weak_ptr, 然后lock提升为shared_ptr, 这样更安全.
         */
        guard = tie_.lock(); // shared_ptr 引用计数+1
        if (guard) // 确保TcpConnection对象还未释放
        {
            handleEventWithGuard(recevieTime);
        }
    }
    else
    {
        handleEventWithGuard(recevieTime);
    }
    //　退出function local 作用域, shared_ptr 引用计数-1
}

/**
 * 根据不同的激活原因, 调用不同的回调函数
 * @details 如果是客户端主动关闭连接, 服务端收到POLLIN事件;
 * 如果是服务端主动关闭连接, 导致连接关闭, 服务端将收到 POLLHUP和POLLIN事件.
 */
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
    eventHandling_ = true; // 正在处理事件
    LOG_TRACE << reventsToString(); // 打印fd及就绪事件
    if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
    { // fd挂起(套接字已不在连接中), 并且没有数据可读
        if (logHup_)
        { // 打印挂起log
            LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
        }
        // 调用关闭回调
        if (closeCallback_) closeCallback_();
    }
    if (revents_ & POLLNVAL) // 无效请求, fd没打开
    { // fd dont be opened
        LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
    }
    if (revents_ & (POLLERR | POLLNVAL)) // 错误条件, 或 无效请求, fd没打开
    { // error or fd dont be opened
        if (errorCallback_) errorCallback_();
    }
    if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) // 有待读数据, 或 紧急数据(e.g. TCP带外数据), 或流套接字对端关闭连接/写半连接
    { // there is data, urgent data,  to be read
        if (readCallback_) readCallback_(receiveTime);
    }
    if (revents_ & POLLOUT)
    {
        if (writeCallback_) writeCallback_();
    }
    eventHandling_ = false;
}

/**
 * 将当前fd及激活事件转化为字符串
 */
string Channel::reventsToString() const
{
    return eventsToString(fd_, revents_);
}

string Channel::eventsToString() const
{
    return eventsToString(fd_, events_);
}

string Channel::eventsToString(int fd, int ev)
{
    std::ostringstream oss;
    oss << fd <<": ";
    if (ev & POLLIN)
        oss << "IN ";
    if (ev & POLLPRI)
        oss << "PRI ";
    if (ev & POLLOUT)
        oss << "OUT ";
    if (ev & POLLHUP)
        oss << "HUP ";
    if (ev & POLLRDHUP)
        oss << "RDHUP ";
    if (ev & POLLERR)
        oss << "ERR ";
    if (ev & POLLNVAL)
        oss << "NVAL ";

    return oss.str();
}

void Channel::remove()
{
    assert(isNoneEvent());
    addedToLoop_ = false;
    loop_->removeChannel(this);
}

void Channel::update()
{
    addedToLoop_ = true;
    loop_->updateChannel(this);
}
